/**
 * @description 常用类型定义: **批注信息**
 * @typedef  {object} Annotation
 * @property {string} name   - 批注名称 唯一标识
 * @property {string} image  - 批注图片base64编码
 * @property {number} page   - 批注插入的页码
 * @property {number} width  - 批注图片宽度
 * @property {number} height - 批注图片宽度
 * @property {number} cx     - 批注图片相对页面左上角的x坐标
 * @property {number} cy     - 批注图片相对页面左上角的y坐标
 */

/**
 * @description 常用类型定义: **右键菜单类型**，枚举， 不同厂家菜单名称有可能不一样，需要做映射
 * @typedef {
 * 'stamp' |
 * 'handtool_tablet' |
 * 'handtool_eraser' |
 * 'handtool_freetext' |
 * 'handtool_delAllAnnot'
 * } ContextMenuEnum
 * @description stamp 橡皮图章
 * @description handtool_tablet 手写签批
 * @description handtool_eraser 橡皮擦
 * @description handtool_freetext 文本框工具
 * @description handtool_delAllAnnot 清空全部注释
 */

/**
 * @description 常用类型定义: **工具栏菜单类型**，枚举，不同厂家工具栏菜单名称有可能不一样，需要做映射
 * @typedef {
 * 'navigator' |
 * 'toolbar' |
 * 'tb_annot' |
 * 'tb_security' |
 * 'e_preference' |
 * 'f_open' |
 * 'f_save' |
 * 'f_saveas' |
 * 'f_print'
 * } MenuBarEnum
 * @description navigator 导航
 * @description toolbar 工具
 * @description tb_annot 批注
 * @description tb_security 安全
 * @description e_preference 首选项
 * @description f_open 打开文件
 * @description f_save 保存文件
 * @description f_saveas 另存为
 * @description f_print 打印
 */

const Constant = {
    CLSID: 'FF1FE7A0-0578-4FEE-A34E-FB21B277D561',
    // 嵌入类型
    EMBED_TYPE: 'application/dj',
}

let installed = false
export default class DianjuOfdReader {
    static installed() {
        if (installed) return installed
        const div = document.createElement('div')
        document.body.appendChild(div)
        const id = Math.random()
        div.innerHTML = `<embed 
            id="${id}"
            width="1px"
            height="1px"
            type="${Constant.EMBED_TYPE}"
            object_id="${id}"
            inited_call="__OFD_OCX_Ready"
        >`
        const ocx = document.getElementById(id)
        const version = ocx.version ? ocx.version() : null
        if (version) {
            console.log(version)
            //ocx.setExit(true)
            ocx.closeBrowser();
        }
        installed = version != null
        document.body.removeChild(div)
        return installed
    }

    /**
     * 插件初始化
     * @param {object} options 插件配置
     * @param {string} options.id 插件容器dom ID
     * @param {string} options.width 插件容器宽度
     * @param {string} options.height 插件容器高度
     * @param {'npapi' | 'websocket'} options.loadType 加载方式 默认 'npapi' 方式
     */
    constructor(options) {
        const container = document.getElementById(options.id)
        if (!container) {
            console.warn('请指定div容器')
            return
        }
        this.containerId = options.id
        this.id = Math.random()
        container.innerHTML = `
        <embed 
            id="${this.id}"
            width="${options.width || '100%'}"
            height="${options.height || '100%'}"
            type="${Constant.EMBED_TYPE}"
            object_id="${this.id}"
            inited_call="__OFD_OCX_Ready"
        >`
        this.ocx = document.getElementById(this.id)

        this.userName = ''
        this.userId = ''
        this.filePath = null
        this.readonly = false
    }

    /**
     * 设置用户信息
     * @param {{ userId: string, userName: string }} userInfo
     */
    setUserInfo(userInfo) {
        if (!this.ocx) return

        this.userName = userInfo.userName
        this.userId = userInfo.userId
        const ocx = this.ocx
        //ocx.setUserName(userInfo.userName)
        // @todo 需要了解清楚用户权限，和客户端注册的关系
        //ocx.SetUserInfoEx(userInfo.userId, 0, 6, 2)
        ocx.SetUserInfoEx(userInfo.userId, 0, 1, userInfo.userName);
        ocx.Login(userInfo.userName, 2, 65535, '', '')
    }

    /**
     * 设置网络接口请求头，用于下载文件，上传文件时，带上授权信息以通过后端服务的接口授权
     * @param {string} name 请求头字段名
     * @param {string} value 值
     */
    setRequestHeader(name, value) {
        this.ocx.setHeaderInfo(name, value);
    }

    /**
     * 设置文件下载超时
     * @param {number} timeout 毫秒数
     */
    setDownloadTimeout(timeout) {
        if (!this.ocx) return
        const ocx = this.ocx
        ocx.setConfigInfo('downloadurl.timeout', timeout)
    }

    /**
     * 设置页面缩放比例
     * @param {number} ratio  100 为原始比例，数值越大，放大倍数越大
     */
    setZoomRatio(ratio) {
        if (!this.ocx) return
        const ocx = this.ocx
        this.zoomRatio = ratio
        ocx.setScale(ratio);
    }

    /**
     * 打开文件 返回成功或失败, 成功: resolve, 失败-reject
     * @param {string} filePath  文件路径
     * @param {boolean} readonly 是否只读
     * @return {Promise<void>}
     */
    async openFile(filePath, readonly) {
        return new Promise((resolve, reject) => {
            const openFileCbName = 'open_file_cb_' + this.containerId
            window[openFileCbName] = (info) => {
                const res = JSON.parse(info)
                console.log('[dianju_openFile]:', res)
                if (res.return_flag === 'true') {
                    resolve()
                } else {
                    reject()
                }
            }
            this.ocx.registListener('f_open', openFileCbName, true)
            // 测试过和数科一样的api
            this.ocx.openFile2(filePath, readonly)
            this.filePath = filePath
            this.readonly = readonly
        })
    }

    /**
     * 保存文件 返回成功或失败, 成功: resolve, 失败-reject
     * @param {string} savePath 保存文件的路径，一般是后端的上传文件接口
     * @return {Promise<void>}
     */
    async saveFile(savePath) {
        return new Promise((resolve, reject) => {
            const saveFileCbName = 'dianju_cb_save_file_' + this.containerId
            window[saveFileCbName] = params => {
                try {
                    console.timeEnd('dianju_saveFile')
                    params = JSON.parse(params || null)
                    if (params.return_flag === 'true') {
                        resolve(JSON.parse(params.httpresponse))
                    } else {
                        reject()
                    }
                } catch (err) {
                    console.log('dianju_saveFile err', err)
                    console.timeEnd('dianju_saveFile')
                    reject(err)
                }
            }
            this.ocx.registListener('f_saveurl', saveFileCbName, true)
            console.time('dianju_saveFile')
            this.ocx.SaveTo(savePath, 'ofd', 0)
        })
    }

    /**
     * 添加批注，即在版式文件中，添加图片批注(橡皮图章)
     * @param {Annotation} annotation
     * @return {Promise<string>} 添加成功返回 节点名称标识
     */
    addAnnotation(annotation) {
        if (!this.ocx) return Promise.reject('ofd插件未初始化！')
        const ocx = this.ocx
        const name = 'stampAnnot_' + this.uuid();
        const ofdPage = this.getPageCount()
        ocx.addStampAnnot(JSON.stringify({
            name,
            image: 'STRDATA:' + annotation.image,
            cx: String(annotation.cx),
            cy: String(annotation.cy),
            pages: String(Math.min(annotation.page, ofdPage) - 1),
            width: String(annotation.width),
            height: String(annotation.height),
            applysign: 'false'
        }))

        const id = `Page${annotation.page}.${name}`
        const json = ocx.getFreeTextInfoById(id)
        if (json) {
            return Promise.resolve(id)
        }
        return Promise.reject('添加批注失败')
    }

    /**
     * 删除批注 成功: resolve, 失败-reject
     * 只能删除该客户所添加的批注节点
     * @param {string} name 批注名称
     * @return {Promise<void>}
     */
    async deleteAnnotation(name) {
        if (!this.ocx) return Promise.reject('ofd插件未初始化！')
        const ocx = this.ocx
        const originLength = this.getAnnotations().length
        ocx.deleteNote(name);
        const newLength = this.getAnnotations().length
        if (newLength === originLength - 1) {
            return Promise.resolve()
        } else {
            return Promise.reject(false)
        }
    }

    /**
     * 获取批注列表, 获取所有批注列表-橡皮图章
     * @return {Array<Annotation>}
     */
    getAnnotations() {
        if (!this.ocx) return []
        const ocx = this.ocx
        try {
            const annotInfo = ocx.getAnnotInfoBy(JSON.stringify({
                userid: '',
                datatag: '',
                annottype: 'Stamp',
                pageindex: 'all',
                annotsubtype: ''
            }))
            try {
                const list = annotInfo ? JSON.parse(annotInfo).datas : []
                console.log('origin data:', list)
                return list.map(item => {
                    /**
                     * @type Annotation
                     */
                    return {
                        cx: Number(item['pos-x']),
                        cy: Number(item['pos-y']),
                        height: Number(item.height),
                        width: Number(item.width),
                        page: Number(item['pageindex']) + 1,
                        name: item.name,
                        image: '',
                    }
                })
            } catch (e) {
                return []
            }
        } catch (e) {
            console.log(e)
            return []
        }
    }

    /**
     * 获取矩形区域内的批注列表
     * @param {object} area 矩形区域对象
     * @param {number} area.page    目标页面
     * @param {number} area.width   矩形区域宽度
     * @param {number} area.height  矩形区域高度
     * @param {number} area.cx      矩形区域相对页面左上角的x坐标
     * @param {number} area.cy      矩形区域相对页面左上角的y坐标
     * @return {Array<Annotation>}
     */
    getAnnotationsByArea(area) {
        if (!this.ocx) return []
        const ocx = this.ocx
        const annotInfo = ocx.getAnnotInfoBy(JSON.stringify({
            userid: '',
            datatag: '',
            annottype: 'all',
            pageindex: 'all',
            annotsubtype: ''
        }))
        /**
         * @type Annotation[]
         */
        const posList = []
        try {
            const page = Math.min(area.page, this.getPageCount())
            const list = annotInfo ? JSON.parse(annotInfo).datas : []
            console.log('origin data:', list)
            list.forEach(item => {
                /**
                 * @type Annotation
                 */
                const annot = {
                    cx: Number(item['pos-x']),
                    cy: Number(item['pos-y']),
                    height: Number(item.height),
                    width: Number(item.width),
                    page: Number(item['pageindex']) + 1,
                    name: item.name,
                    image: '',
                }

                // 判断节点在区域内
                if (page === annot.page &&
                    area.cx <= annot.cx + annot.width &&
                    area.cy <= annot.cy + annot.height &&
                    area.cx + area.width >= annot.cx &&
                    area.cy + area.height >= annot.cy
                ) {
                    posList.push(annot)
                }
            })
            return posList
        } catch (e) {
            return posList
        }
    }

    /**
     * 获取base64格式的文件数据
     */
    getBase64Data() {
        if (!this.ocx) return null
        const ocx = this.ocx
        return ocx.GetData()
    }

    /**
     * 获取文件页数
     * @return {number}
     */
    getPageCount() {
        if (!this.ocx) return 0
        const ocx = this.ocx
        return ocx.getPageCount()
    }

    /**
     * 跳转到指定位置，一般是用于添加批注时，页面聚焦于批注的位置
     * @param {number} page 页码
     * @param {number} cx   页面左上角的x坐标
     * @param {number} cy   页面左上角的y坐标
     */
    gotoDest(page, cx, cy) {
        if (!this.ocx) return
        // @todo api未生效
        this.ocx.GotoPosition(4, "", page - 1, cx, cy);
    }

    /**
     * 页面缩放
     * @param {'in' | 'out'} type in - 放大，out - 缩小
     */
    zoom(type) {
        if (!this.ocx) return
        const ocx = this.ocx
        const percent = ocx.GetZoomPrecent();
        if (percent <= 0) {
            return;
        }
        switch (type) {
            case 'in':
                ocx.SetPageMode(1, percent + 10);
                break;//每次+10%
            case 'out':
                ocx.SetPageMode(1, percent - 10);
                break;//每次-10%
        }
    }

    /**
     * 设置右键菜单@ㅤ阿泰
     * SetContextMenuStripVisible("contextmenu_tool_picstamp","true"); //图章
     * SetContextMenuStripVisible("contextmenu_tool_tablet","true");	//手写签批
     * SetContextMenuStripVisible("contextmenu_tool_tabletdel","true"); //橡皮擦
     * SetContextMenuStripVisible("contextmenu_tool_freetext","true");	//文本框
     * SetContextMenuStripVisible("contextmenu_delete_allannot","true");	//清空全部注释
     * @param {Array<ContextMenuEnum>} menus 需要控制的右键菜单数组
     * @param {boolean} visible  是否显示右键菜单
     */
    setContextMenuVisible(menus, visible) {
        if (!this.ocx) return
        const ocx = this.ocx
        if (Array.isArray(menus) && menus.length) {
            menus.forEach(item => {
                let menu = null;
                switch (item) {
                    case 'stamp':
                        ocx.SetContextMenuStripVisible("contextmenu_annot_delete", String(visible));
                        ocx.SetContextMenuStripVisible("contextmenu_annot_property", String(visible));
                        break;
                    case 'handtool_tablet':
                        menu = 'contextmenu_tool_tablet';
                        break;
                    case 'handtool_eraser':
                        menu = 'contextmenu_tool_tabletdel';
                        break;
                    case 'handtool_freetext':
                        menu = 'contextmenu_tool_freetext';
                        break;
                    case 'handtool_delAllAnnot':
                        menu = 'contextmenu_delete_allannot';
                        break;
                }

                if (menu) {
                    ocx.SetContextMenuStripVisible(menu, String(visible));
                }

            })
        }
    }

    /**
     * 设置工具栏组件是否可见
     *  * @description navigator 导航
     *  * @description toolbar 工具
     *  * @description tb_annot 批注
     *  * @description tb_security 安全
     *  * @description e_preference 首选项
     *  * @description f_open 打开文件
     *  * @description f_save 保存文件
     *  * @description f_saveas 另存为
     *  * @description f_print 打印
     * @param {Array<MenuBarEnum>} items 工具菜单元素名称数组
     * @param {boolean} visible 是否可见
     */
    setCompositeVisible(items, visible) {
        if (!this.ocx) return
        const ocx = this.ocx
        const ms = ['toolbar_seal', 'toolbar_page', 'toolbar_edit', 'toolbar_page', 'toolbar_view', 'toolbar_base', 'toolbar_freetext', 'toolbar_mask',
            'toolbar_watermark', 'toolbar_revision', 'toolbar_other', 'toolbar_zoom', 'toolbar_annot', 'toolbar_tablet', 'file_export'];
        ms.forEach(a => {
            ocx.setCompositeVisible(a, false);
        })
        if (Array.isArray(items) && items.length) {
            var menu = null;
            items.forEach(item => {
                switch (item) {
                    case 'tb_annot':
                        menu = 'toolbar_tablet';
                        break;//批注工具
                    case 'f_open':
                        menu = 'file_open';
                        break;
                    case 'f_save':
                        menu = 'file_save';
                        break;
                    case 'f_saveas':
                        menu = 'file_saveas';
                        break;
                    case 'f_print':
                        menu = 'file_print';
                        break;
                    default:
                        menu = item;
                }
                ocx.setCompositeVisible(menu, visible);
            })
        }
    }

    /**
     * 打印文件
     * @param {boolean} isGrayPrint 是否灰度打印
     */
    printFile(isGrayPrint) {
        if (!this.ocx) return
        const ocx = this.ocx
        const printCbName = `cb_print_${this.containerId}`
        window[printCbName] = params => {
            try {
                params = JSON.parse(params || null)
            } catch (err) {
                console.log('dianju_print err', err)
            }
            console.log('dianju_print', params)
        }
        ocx.registListener('file_print', printCbName, true)
        ocx.setPrintCompsiteChecked("Print/set.gray", isGrayPrint);//灰度打印按钮是否勾选
        ocx.setPrintCompsiteEnable("Print/set.gray.enable", !isGrayPrint);//灰度打印按钮是否可勾选
        ocx.PrintDoc(0, 1, '');
    }

    /**
     * 下载灰度文件
     * @param {string} fileName 需要下载保存的文件名
     */
    downloadGrayFile(fileName) {
        console.log(fileName)
        // if (!this.ocx) return
        const ocx = this.ocx
        ocx.GrayData(31);
        const s = ocx.ShowDialog(2, "", fileName, "ofd文件|*.ofd|");
        if (s) {
            ocx.SaveTo(s, fileName.split('.').pop(), 0);
        }
        this.closeFile()
        this.openFile(this.filePath, this.readonly)
    }

    /**
     * 盖章操作
     */
    signSeal() {
        if (!this.ocx) return
        const ocx = this.ocx
        ocx.performClick('t_sealsign')
    }

    /**
     * 模拟点击
     * @param {string} command 需要执行的命令
     */
    performClick(command) {
        if (!this.ocx) return
        const ocx = this.ocx
        ocx.performClick(command)
    }

    /**
     * 关闭文件
     */
    closeFile() {
        this.ocx.closeFile(true)
    }

    /**
     * 插件销毁，执行清理工作
     */
    destroy() {
        if (!this.ocx) return
        //his.ocx.setExit(true)
        this.ocx.closeBrowser();
        document.getElementById(this.containerId).removeChild(this.ocx)
        this.ocx = null
    }
}
